package com.hzw.saas.service.msg.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hzw.saas.api.group.IGroupMemberService;
import com.hzw.saas.api.msg.IMsgOperationService;
import com.hzw.saas.api.msg.IMsgService;
import com.hzw.saas.api.msg.IMsgStatusService;
import com.hzw.saas.api.msg.bo.MsgActionBo;
import com.hzw.saas.api.msg.bo.MsgBo;
import com.hzw.saas.api.msg.bo.MsgCountBo;
import com.hzw.saas.api.msg.bo.MsgOperationBo;
import com.hzw.saas.api.msg.enums.MsgRead;
import com.hzw.saas.api.msg.enums.MsgType;
import com.hzw.saas.api.msg.event.SendMsgEvent;
import com.hzw.saas.api.msg.query.MsgOperationQuery;
import com.hzw.saas.api.msg.query.MsgQuery;
import com.hzw.saas.api.msg.query.MsgSearchQuery;
import com.hzw.saas.api.msg.query.MsgStatusQuery;
import com.hzw.saas.common.config.util.AssertUtil;
import com.hzw.saas.common.config.util.PageParam;
import com.hzw.saas.common.util.IdWorker;
import com.hzw.saas.common.util.Json;
import com.hzw.saas.service.msg.mapper.MsgMapper;
import com.hzw.saas.service.msg.mapper.MsgStatusMapper;
import com.hzw.saas.service.msg.model.Msg;
import com.hzw.saas.service.msg.model.MsgStatus;
import jodd.util.StringUtil;
import lombok.RequiredArgsConstructor;
import ma.glasnost.orika.MapperFacade;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * <p>
 * 用户消息表 服务实现类
 * </p>
 *
 * @author sonam
 * @since 2020-12-22
 */
@RequiredArgsConstructor
@Service("msgServiceImpl")
public class MsgServiceImpl extends ServiceImpl<MsgMapper, Msg> implements IMsgService {

    private final ApplicationEventPublisher applicationEventPublisher;
    private final MapperFacade mapperFacade;

    @Resource(name = "msgOperationServiceImpl")
    private IMsgOperationService msgOperationService;
    @Resource(name = "msgStatusServiceImpl")
    private IMsgStatusService msgStatusService;
    @Resource(name = "groupMemberServiceImpl")
    private IGroupMemberService groupMemberService;

    private final MsgStatusMapper msgStatusMapper;
    private final MsgMapper msgMapper;
    private final IdWorker idWorker;

    @Resource(name = "msgServiceImpl")
    private IMsgService self;

    @Override
    @Cacheable(cacheNames = {"msg"}, key = "#msgId")
    public MsgBo getMsgById(String msgId) {
        Msg userMsg = getById(msgId);
        MsgBo msgDto = mapperFacade.map(userMsg, MsgBo.class);
        return msgDto;
    }

    @CachePut(cacheNames = {"msg"}, key = "#result.msgId")
    @Override
    @Transactional(rollbackFor = Exception.class)
    public MsgBo addMsg(MsgQuery msgQuery) {
        Msg msg = mapperFacade.map(msgQuery, Msg.class);
        // 提交一个发送消息的事件
        applicationEventPublisher.publishEvent(new SendMsgEvent(msg.getTo(), msg.getFrom(), msgQuery.getMsgType()));
        return forceAddMsg(msgQuery);
    }

    @Override
    @CachePut(cacheNames = {"msg"}, key = "#result.msgId")
    public MsgBo forceAddMsg(MsgQuery msgQuery) {
        Msg msg = mapperFacade.map(msgQuery, Msg.class);
        // 构造消息通用属性
        if(StringUtil.isBlank(msg.getMsgId())) {
            msg.setMsgId(String.valueOf(idWorker.nextId()));
        }
        if(Objects.isNull(msg.getDate())) {
            msg.setDate(new Date());
        }

        // 保存消息
        save(msg);

        // 是否需要插入业务信息
        List<MsgActionBo> msgActionDtoList = msgQuery.getMsgActionDtoList();
        if (CollectionUtils.isNotEmpty(msgActionDtoList)) {
            MsgOperationQuery msgOperation = new MsgOperationQuery();
            msgOperation.setId(String.valueOf(idWorker.nextId()));
            msgOperation.setMsgId(msg.getMsgId());

            /** 将消息操作id，自动放入业务参数里*/
            msgActionDtoList.forEach(msgActionDto -> {
                msgActionDto.getData().put("msgOperationId", msgOperation.getId());
            });
            msgOperation.setAction(Json.toJsonString(msgActionDtoList));
            msgOperationService.addMsgOperator(msgOperation);

            MsgBo msgDto = mapperFacade.map(msg, MsgBo.class);
            MsgOperationBo msgOperationDto = mapperFacade.map(msgOperation, MsgOperationBo.class);
            msgOperationDto.setMsgActionDtoList(msgActionDtoList);
            msgDto.setMsgOperationDto(msgOperationDto);

            // TODO 消息退推送
//            msgPushService.inviteMsgPushToUser(msgDto);
        }

        return mapperFacade.map(msg, MsgBo.class);
    }

    @CacheEvict(cacheNames = {"msg"}, key = "#msgId")
    @Override
    public boolean deleteMsg(String msgId) {
        return removeById(msgId);
    }

    @Override
    @Transactional
    public boolean deleteGroupMsg(List<String> groupIds) {
        LambdaQueryWrapper<Msg> eq = Wrappers.<Msg>lambdaQuery()
            .eq(Msg::getType, MsgType.GROUP_MSG.code())
            .and(wrapper -> {
                wrapper.in(Msg::getTo, groupIds)
                    .or().in(Msg::getFrom, groupIds);
            });

        List<Msg> msgList = msgMapper.selectList(eq);
        if(CollectionUtils.isEmpty(msgList)) {
            return true;
        }

        List<String> msgIds = msgList.stream().map(Msg::getMsgId).collect(Collectors.toList());
        // 删除业务信息
        msgOperationService.deleteMsgOperatorByMsgIds(msgIds);
        // 删除消息
        msgMapper.deleteBatchIds(msgIds);
        // 删除该群组的消息状态
        msgStatusMapper.delete(Wrappers.<MsgStatus>lambdaQuery().in(MsgStatus::getMsgId, msgIds));
        return  true;
    }


    @Override
    @Transactional
    public void setMsgRead(String msgId, String userId, MsgRead msgRead) {
        /** 验证消息是否有权限*/
        MsgBo msgBo = self.getMsgById(msgId);
        switch (MsgType.instance(msgBo.getType())) {
            case SYSTEM_MSG:
                // 系统消息
                if (Objects.equals("-1", msgBo.getTo()) || msgBo.getTo() == null) {
                    break;
                }
            case USER_MSG:
                // 个人消息
                // 验证 to 是否合理
                AssertUtil.assertThrow("设置消息已读状态失败，你没有访问该消息的权限",
                    HttpStatus.FORBIDDEN,
                    !Objects.equals(userId, msgBo.getTo()));
                break;
            case GROUP_MSG:
                // to可能是 groupId或者 userId
                if (!Objects.equals(userId, msgBo.getTo())) {
                    // 验证群组消息
                    groupMemberService.tryJoinGroup(userId, msgBo.getTo());
                }
                break;
        }

        MsgStatus msgStatus = msgStatusMapper.selectOne(Wrappers.<MsgStatus>lambdaQuery()
            .eq(MsgStatus::getMsgId, msgId)
            .eq(MsgStatus::getUserId, userId));

        if (msgStatus == null) {
            // 插入一条新的数据
            MsgStatus newMsgStatus = new MsgStatus();
            newMsgStatus.setRead(msgRead.code());
            newMsgStatus.setUserId(userId);
            newMsgStatus.setMsgId(msgId);
            msgStatusMapper.insert(newMsgStatus);
            return;
        }

        if (MsgRead.instance(msgStatus.getRead()) == msgRead) {
            // 和数据库一样，直接返回
            return;
        }

        /** 更新状态*/
        msgStatus.setUserId(null);
        msgStatus.setMsgId(null);
        msgStatus.setRead(msgRead.code());
        msgStatusMapper.updateById(msgStatus);
    }

    @Override
    @Transactional
    public void setMsgAllRead(String userId) {
        List<String> msgIds = this.getAllMsgIdList(userId, MsgRead.UNREAD);
        if(CollectionUtils.isEmpty(msgIds)) {
            return;
        }

        // 先删除全部
        msgStatusMapper.delete(Wrappers.<MsgStatus>lambdaQuery().in(MsgStatus::getMsgId, msgIds).eq(MsgStatus::getUserId, userId));

        // 統一设置为已读
        List<MsgStatus> msgStatusList = new ArrayList<>();
        msgIds.forEach(msgId -> {
            MsgStatus msgStatus = new MsgStatus();
            msgStatus.setMsgId(msgId);
            msgStatus.setUserId(userId);
            msgStatus.setRead(MsgRead.READ.code());
            msgStatus.setIsDelete(0);
            msgStatusList.add(msgStatus);
        });

        // 批量插入已读，而不是更新
        if (CollectionUtils.isNotEmpty(msgStatusList)) {
            msgStatusList.forEach(msgStatus -> {
                msgStatusMapper.insert(msgStatus);
            });
        }
    }

    @Override
    public IPage<MsgBo> findAllMsgByUserIdAndTypeAndRead(PageParam pageParam, MsgSearchQuery msgSearchQuery) {
        List<String> msgIds = new ArrayList<>();
        MsgRead msgRead = msgSearchQuery.getMsgRead();
        String userId = msgSearchQuery.getUserId();

        switch (msgSearchQuery.getMsgType()) {
            case GROUP_MSG:
                // 获取所有群组的消息id
                msgIds.addAll(getGroupMsgIds(getGroupIdsByUserId(userId), userId, msgRead));
                break;
            case USER_MSG:
            case SYSTEM_MSG:
                // TODO 后面如果系统消息和个人消息分开查询，请修改这里
                msgIds.addAll(getSystemMsgIds(userId, msgRead));
                msgIds.addAll(getUserMsgIds(userId, msgRead));
                break;
        }

        if (CollectionUtils.isEmpty(msgIds)) {
            return new PageParam().convert();
        }

        IPage<MsgBo> msgDtoPage = msgMapper.findAllMsgByMsgIdList(pageParam.convert(), msgIds);

        // 处理查询出来的业务信息
        msgDtoPage.getRecords().forEach(msgDto -> {
            if(msgDto.getRead() == null) {
                msgDto.setRead(MsgRead.UNREAD.code());
            }

            MsgOperationBo msgOperationDto = msgDto.getMsgOperationDto();
            if (msgOperationDto == null) {
                return;
            }

            String action = msgOperationDto.getAction();
            if (StringUtil.isBlank(action)) {
                // 业务文本为null，跳过
                msgDto.setMsgOperationDto(null);
                return;
            }

            try {
                // 解析业务文本
                List<MsgActionBo> msgActionList = MsgActionBo.parse(action);
                msgOperationDto.setMsgActionDtoList(msgActionList);
                msgOperationDto.setAction(null);
            } catch (Exception e) {
                e.printStackTrace();
                // 业务文本解析失败。
                msgDto.setMsgOperationDto(null);
            }
        });

        return msgDtoPage;
    }

    @Override
    public MsgCountBo getMsgCountByUserId(String userId) {
        // 计算群组消息数量
        List<String> groupMsgIds = getGroupMsgIds(getGroupIdsByUserId(userId), userId, MsgRead.UNREAD);
        int msgGroupCount = groupMsgIds.size();

        // 计算系统消息数量
        List<String> systemMsgIds = getSystemMsgIds(userId, MsgRead.UNREAD);
        int msgSystemCount = systemMsgIds.size();

        // 计算个人消息数量
        List<String> userMsgIds = getUserMsgIds(userId, MsgRead.UNREAD);
        int msgUserCount = userMsgIds.size();

        // 总数
        int total = msgGroupCount + msgSystemCount + msgUserCount;

        MsgCountBo msgCountDto = new MsgCountBo();
        msgCountDto.setTotal(total);
        msgCountDto.setGroupMsgCount(msgGroupCount);
        msgCountDto.setUserMsgCount(msgUserCount);
        msgCountDto.setSystemMsgCount(msgSystemCount);
        return msgCountDto;
    }

    @Override
    public List<String> getGroupMsgIds(List<String> groupIds, String userId, MsgRead msgRead) {
        if (CollectionUtils.isEmpty(groupIds)) {
            return new ArrayList<>();
        }

        return msgMapper.findAllMsgIdByMsgTypeAndMsgReadAndUserId(groupIds, userId, MsgType.GROUP_MSG, msgRead);
    }

    /**
     * 获取当前用户所有系统消息ids
     *
     * @param userId
     * @return
     */
    @Override
    public List<String> getSystemMsgIds(String userId, MsgRead msgRead) {
        return msgMapper.findAllMsgIdByMsgTypeAndMsgReadAndUserId(null, userId, MsgType.SYSTEM_MSG, msgRead);
    }

    /**
     * 获取当前用户所有个人消息ids
     *
     * @param userId
     * @return
     */
    @Override
    public List<String> getUserMsgIds(String userId, MsgRead msgRead) {
        return msgMapper.findAllMsgIdByMsgTypeAndMsgReadAndUserId(null, userId, MsgType.USER_MSG, msgRead);
    }

    @Override
    public List<String> getAllMsgIdList(String userId, MsgRead msgRead) {
        List<String> msgIds = new ArrayList<>();
        msgIds.addAll(getGroupMsgIds(getGroupIdsByUserId(userId), userId, msgRead));
        msgIds.addAll(getSystemMsgIds(userId, msgRead));
        msgIds.addAll(getUserMsgIds(userId, msgRead));
        return msgIds;
    }

    @Override
    @Transactional
    public void deleteMsgByMsgIdsAndUserId(List<String> msgIds, String userId) {
        if(CollectionUtils.isEmpty(msgIds)) {
            return;
        }
        // 获取当前所有消息状态
        List<MsgStatus> msgStatuses = msgStatusMapper.selectList(Wrappers.<MsgStatus>lambdaQuery()
            .eq(MsgStatus::getUserId, userId)
            .in(MsgStatus::getMsgId, msgIds));
        // 将所有存在的消息，设置为删除状态
        msgStatuses.forEach(msgStatus -> msgStatus.setIsDelete(1));

        // 获取所有存在消息状态的ID集合
        List<String> existMsgIds = msgStatuses.stream().map(MsgStatus::getMsgId).collect(Collectors.toList());
        msgIds.forEach(msgId -> {
            if (!existMsgIds.contains(msgId)) {
                // 构造新的消息状态
                MsgStatus msgStatus = new MsgStatus();
                msgStatus.setIsDelete(1);
                msgStatus.setRead(MsgRead.UNREAD.code());
                msgStatus.setUserId(userId);
                msgStatus.setMsgId(msgId);
                msgStatuses.add(msgStatus);
            }
        });

        // 批量更新或者插入
        msgStatusService.saveOrUpdateBatch(mapperFacade.mapAsList(msgStatuses, MsgStatusQuery.class));

        // 删除MsgOperator数据
        msgOperationService.deleteMsgOperatorByMsgIds(msgStatuses.stream().map(MsgStatus::getMsgId).collect(Collectors.toList()));
    }

    private List<String> getGroupIdsByUserId(String userId) {
        return groupMemberService.getGroupIdListByUserId(userId);
    }

}
